//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
#include <elasys_server.h>
#include <stdio.h>
#include <stdlib.h>

#define SECTION_ENVIRONMENT L"environment"
#define DEFAULT_CONFIG_FILENAME L"carhost.cfg"

EXTERN_C int CDECL _PutEnvironment(const wchar_t *envstring);
EXTERN_C wchar_t * CDECL _GetEnvironment(const wchar_t *envstring);
extern "C" void* __cdecl Aura_malloc(size_t size);

INLINE wchar_t * SkipHeadSpace(wchar_t *pwszValue)
{
    for (;' ' == *pwszValue || '\t' == *pwszValue; pwszValue++);
    return pwszValue;
}

INLINE void SkipTailSpace(wchar_t *pwszValue)
{
    for (;' ' == *pwszValue || '\t' == *pwszValue; pwszValue--);
    if (*(pwszValue + 1) != EOS) {
        *(pwszValue + 1) = EOS;
    }
}

static wchar_t * ScanToken(
    wchar_t *pwszStr, // the string to lexically analyze
    const wchar_t wchTermSym // a terminal symbol (list)
    )
{
    for (; *pwszStr; pwszStr++) {
        if (*pwszStr == wchTermSym) {
            *pwszStr = EOS;
            return pwszStr;
        }
    }
    return NULL;// Not found
}

//BUGBUG: if there is too much parameter to be deploied, _MAX_LINE may be short
wchar_t *EnvParaSwitchover(wchar_t *pwszEnv, wchar_t wchToken)
{
    if(NULL == pwszEnv)return NULL;
    wchar_t *pwszPara = NULL;
    wchar_t *pwszTail = pwszEnv;
    wchar_t * pwszEnvValue = NULL;
    wchar_t *pwszRet = (wchar_t *)Aura_malloc(sizeof(wchar_t) * _MAX_LINE +1);
    if (!pwszRet) {
        printf("Not Enough Memory!\n");
        return NULL;
    }
    *pwszRet = NULL;
    while (1) {
        pwszPara = ScanToken(pwszTail, wchToken);
        if(NULL == pwszPara) break;
        wcscat(pwszRet, pwszTail);
        pwszPara++;
        pwszTail = ScanToken(pwszPara, wchToken);
        if(NULL == pwszTail) return NULL;
        pwszTail++;
        if (L'%' == wchToken) {
            pwszEnvValue= _GetEnvironment(pwszPara);
            if (NULL == pwszEnvValue) {
                continue;
            }
            wcscat(pwszRet, pwszEnvValue);
        }
        else if (L'$' == wchToken) {
            pwszEnvValue = _wgetenv(pwszPara);
            if (NULL == pwszEnvValue) {
                continue;
            }
            wcscat(pwszRet, pwszEnvValue);
        }
        else
            return NULL;
    }//while
    wcscat(pwszRet, pwszTail);
    return pwszRet;
}

//put "-cfg" or "-dev" in pwszCompare to choice the Parameter
bool GetParameter(wchar_t* pFilename, wchar_t* pwszCompare)
{
    wchar_t *cmdLine;
    cmdLine = GetCommandLineW();
    cmdLine = wcsstr(cmdLine, pwszCompare);
    if (NULL != cmdLine) {
        cmdLine += wcslen(pwszCompare);
        cmdLine = SkipHeadSpace(cmdLine);
        for (; (*cmdLine != '\0') && (*cmdLine != ' ');) {
            *pFilename = *cmdLine;
            cmdLine++;
            pFilename++;
        }
        *pFilename = '\0';
        return true;
    }
    return false;
}

Boolean AddDefaultPath(/* out */ wchar_t *pwszFileName)
{
    wchar_t *szFilePath;
    wchar_t szTempName[_MAX_PATH];

    wcscpy(szTempName, pwszFileName);

    // Get the full path of the config file
    szFilePath = _wgetenv(L"XDK_SIMULATOR_RESOURCE");
    if (szFilePath) {
        // The file is in the special config directory
        //wprintf(L"in AddDefaultPath EMULATOR_RESOURCE %s\n", szFilePath);
        wcscpy(pwszFileName, szFilePath);
    }
    else {
        // Think the file is in the current directory
        wcscpy(pwszFileName, L".");
    }
    wcscat(pwszFileName, L"\\");
    wcscat(pwszFileName, szTempName);
    assert(wcslen(pwszFileName) < _MAX_PATH);

    return TRUE;
}

// Read a section in config file
static Boolean ReadSection(FILE *pCfgFile, wchar_t *pwszSecName)
{
    wchar_t wszLine[_MAX_LINE + 1];

    memset(wszLine, 0, sizeof(wszLine));

    if (fseek(pCfgFile, 0, SEEK_SET)) {
        wprintf(L"Error in seek device file!\n");
        return FALSE;
    }
    //STEP 1. Find Section

    while (fgetws(wszLine, _MAX_LINE, pCfgFile)) {
        wchar_t *pwszSecBegin = wszLine;

        pwszSecBegin = SkipHeadSpace(pwszSecBegin);

        if (*pwszSecBegin == L'#' ||
            *pwszSecBegin == L'\n' || *pwszSecBegin == EOS)
            continue; //skip comment
        else if (*pwszSecBegin++== L'[') {//Section Begin
#if defined(_DEBUG)
            wchar_t *pwszSecEnd =
#endif
            ScanToken(pwszSecBegin, L']');
            assert(pwszSecEnd);
            if (0 == wcscmp(pwszSecName, pwszSecBegin))
                goto FoundSection; //GOT
        }
    }
    return FALSE;

FoundSection:
    //STEP 2. Find Field
    while (fgetws(wszLine, _MAX_LINE, pCfgFile)) {
        wchar_t *pwszFldBegin, *pwszFldEnd;
        pwszFldBegin = wszLine;

        pwszFldBegin = SkipHeadSpace(pwszFldBegin);

        // skip comment
        if (*pwszFldBegin == L'[') return TRUE; // End of Section

        if (*pwszFldBegin == L'#' ||
            *pwszFldBegin == L'\n' || *pwszFldBegin == EOS)
            continue;

        //some especial disposal in enviroment
        if (!wcscmp(SECTION_ENVIRONMENT, pwszSecName)) {
            wchar_t *pwszTail = pwszFldBegin + wcslen(pwszFldBegin) - 1;
            while (*pwszTail == L'\r' || *pwszTail == L'\n') {
                *pwszTail = EOS;
                pwszTail--;
            }

            //inspect the enviroment parameter
            pwszFldEnd = ScanToken(pwszFldBegin, L'=');
            if (NULL == pwszFldEnd) {
                continue;
            }
            else {
                *pwszFldEnd = L'=';
            }

            pwszFldBegin = EnvParaSwitchover(pwszFldBegin, L'%');
            if (NULL == pwszFldBegin) {
                ////wprintf(L"Read one enviroment Parameter error %\n");
                continue;
            }
            pwszFldBegin = EnvParaSwitchover(pwszFldBegin, L'$');
            if (NULL == pwszFldBegin) {
                ////wprintf(L"Read one enviroment Parameter error $\n");
                continue;
            }

            _PutEnvironment(pwszFldBegin);
            continue;
        }
    }

    return TRUE;
}

EXTERN_C Boolean CDECL DeployEnvironString()
{
    FILE *pCfgFile;
    wchar_t wszCfgFileName[_MAX_PATH];

    //get config file name from command line
    //TODO: support adding environment string from arg
    if (GetParameter(wszCfgFileName, L"-cfg")) {
        //open the appointed CfgFile
        pCfgFile = _wfopen(wszCfgFileName, L"r");
        if (pCfgFile) goto READCFG;

        //open the added default path CfgFile
        AddDefaultPath(wszCfgFileName); // Not recommended
        pCfgFile = _wfopen(wszCfgFileName, L"r");
        if (pCfgFile) goto READCFG;

        ////wprintf(L"Can not open appointed config file!\n");
    }
    //open default config file
    wcscpy(wszCfgFileName, DEFAULT_CONFIG_FILENAME);
    AddDefaultPath(wszCfgFileName);

    pCfgFile = _wfopen(wszCfgFileName, L"r");
    if (!pCfgFile) {
        wprintf(L"Error in open config file!\n");
        return FALSE;
    }

READCFG:
    //add environment string from file
    ReadSection(pCfgFile, SECTION_ENVIRONMENT);

    //TODO: add environment string from arg

    //add emulator environment "path" to windows
    wchar_t *pwszWinPath = _wgetenv(L"path");
    wchar_t *pwszSimPath = _GetEnvironment(L"path");
    if (NULL == pwszWinPath) pwszWinPath = L"";

    if (NULL != pwszSimPath) {
        wchar_t *pwszNewWinPath = (wchar_t *)Aura_malloc(sizeof(wchar_t)
                            * (wcslen(pwszWinPath) + wcslen(pwszSimPath) + 10));
        if (pwszNewWinPath == NULL) {
            return FALSE;
        }
        wcscpy(pwszNewWinPath, L"path=");
        wcscat(pwszNewWinPath, pwszWinPath);
        wcscat(pwszNewWinPath, L";"); // May environment "path" isn't ended with ';'
        wcscat(pwszNewWinPath, pwszSimPath);
        _wputenv(pwszNewWinPath);
    }

    wchar_t wszEnvStr[_MAX_PATH];
    wchar_t *pwszCD = _wgetenv(L"CD");
    wcscpy(wszEnvStr, L"CD=");

    if (!pwszCD) {
        wchar_t wszPathName[_MAX_PATH];
        GetCurrentDirectoryW(_MAX_PATH, wszPathName);
        wcscat(wszEnvStr, wszPathName);
    }
    else {
        wcscat(wszEnvStr, pwszCD);
    }
    
    wchar_t *pEnvStr = wszEnvStr;
    while (*pEnvStr != L'\0') {
        if (*pEnvStr == L'\\') {
            *pEnvStr = L'/';
        }
        pEnvStr++;
    }

    _PutEnvironment(wszEnvStr);
    _wputenv(wszEnvStr);

    return TRUE;
}
